home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C ++ / Frameworks / MacZoop 1.6.5 / More Classes / File Classes / ZFolderScanner.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-30  |  14.5 KB  |  573 lines  |  [TEXT/CWIE]

  1. /*************************************************************************************************
  2. *
  3. *
  4. *            ObjectMacZapp        -- a standard Mac OOP application template
  5. *
  6. *
  7. *
  8. *            ZFolderScanner.cpp    -- a generic object for recursively searching folders
  9. *
  10. *
  11. *
  12. *
  13. *
  14. *            Ā© 1996, Graham Cox
  15. *
  16. *
  17. *
  18. *
  19. *************************************************************************************************/
  20.  
  21.  
  22. #include    "ZFolderScanner.h"
  23. #include    "ZProgress.h"
  24. #include    "MacZoop.h"
  25.  
  26. /*--------------------------------***  CONSTRUCTOR  ***---------------------------------*/
  27.  
  28. ZFolderScanner::ZFolderScanner( const FSSpec& rootFolder )
  29.     : ZFile( rootFolder )
  30. {
  31.     Boolean        isDirectory;
  32.     
  33.     // check that the spec is a folder- throws exception if not.
  34.     
  35.     FailOSErr( FSpGetDirectoryID ( &itsSpec, &topDirID, &isDirectory ));
  36.     FailOSErr( (isDirectory)? noErr : paramErr );
  37.     
  38.     curDepth = 0;
  39.     searchDepth = kDefaultSearchDepth;    
  40.     useProgressDialog = TRUE;
  41. }
  42.  
  43. /*--------------------------------***  CONSTRUCTOR  ***---------------------------------*/
  44.  
  45.  
  46. ZFolderScanner::ZFolderScanner()
  47.     : ZFile( "\p" )
  48. {
  49.     // assume the filespec for the start-up hard disk.
  50.     
  51.     HParamBlockRec    hb;
  52.     
  53.     hb.volumeParam.ioCompletion = NULL;
  54.     hb.volumeParam.ioVolIndex = 1;
  55.     hb.volumeParam.ioNamePtr = fName;
  56.     fName[0] = 0;
  57.     
  58.     FailOSErr( PBHGetVInfoSync( &hb ));
  59.     
  60.     // make a filespec. This is a volume, so to make sure all goes
  61.     // OK, we actually force the volume name to have an appended colon
  62.     // and set the parent ID to -2.
  63.     
  64.     itsSpec.vRefNum = hb.volumeParam.ioVRefNum;
  65.     itsSpec.parID = -2;
  66.     BlockMoveData(fName, itsSpec.name, fName[0] + 1);
  67.     itsSpec.name[fName[0] + 1] = ':';
  68.     itsSpec.name[0] += 1;
  69.  
  70.     curDepth = 0;
  71.     searchDepth = kDefaultSearchDepth;    
  72.     useProgressDialog = TRUE;
  73. }
  74.  
  75.  
  76. /*---------------------------------***  DESTRUCTOR  ***---------------------------------*/
  77.  
  78. ZFolderScanner::~ZFolderScanner()
  79. {
  80. }
  81.  
  82.  
  83. /*------------------------------***  SETSEARCHDEPTH  ***-------------------------------*/
  84. /*
  85. set how many levels you want to recurse to when scanning. The default is 0, meaning only
  86. scan the folder and no folders within it. Passing -1 means search every folder no matter
  87. how deep the search goes. Other positive values set the scanning depth.
  88. ---------------------------------------------------------------------------------------*/
  89.  
  90. void    ZFolderScanner::SetSearchDepth( const short aSearchDepth )
  91. {
  92.     searchDepth = aSearchDepth;
  93. }
  94.  
  95. /*--------------------------------***  PICKFOLDER  ***---------------------------------*/
  96. /*
  97. presents an interface for selecting a folder to scan
  98. ---------------------------------------------------------------------------------------*/
  99.  
  100. Boolean    ZFolderScanner::PickFolder()
  101. {
  102.     FSSpec    aSpec;
  103.     Boolean    result = FALSE;
  104.     
  105.     gWindowManager->Deactivate();
  106.     
  107.     if( ChooseFolder( &aSpec ))
  108.     {
  109.         itsSpec = aSpec;
  110.         result = TRUE;
  111.     }
  112.     
  113.     gWindowManager->Activate();
  114.     
  115.     return result;
  116. }
  117.  
  118.  
  119. /*--------------------------------***  SCANFOLDER  ***---------------------------------*/
  120. /*
  121. public call to kick off the folder scan after setting it up.
  122. ---------------------------------------------------------------------------------------*/
  123.  
  124. void    ZFolderScanner::ScanFolder()
  125. {
  126.     Boolean    isDirectory;
  127.     
  128.     // set up the <pb> data member for scanning
  129.  
  130.     pb.hFileInfo.ioNamePtr = fName;
  131.     pb.hFileInfo.ioVRefNum = itsSpec.vRefNum;
  132.     fName[0] = 0;
  133.     curDepth = 0;
  134.     
  135.     FailOSErr( FSpGetDirectoryID( &itsSpec, &topDirID, &isDirectory ));
  136.     FailOSErr( (isDirectory)? noErr : paramErr );
  137.     
  138.     // set up progress dialog, if wanted
  139.     
  140.     if( useProgressDialog )
  141.     {
  142.         FailNIL( itsPD = new ZProgress( gApplication, kStdProgressResID, _STRIPED_BAR, kIndeterminateProgress, kStopType ));
  143.         itsPD->SetTitle( itsSpec.name );
  144.     }
  145.     
  146.     // call Scan1Folder, which recurses to the set depth if need be.
  147.     
  148.     try
  149.     {
  150.         Scan1Folder( topDirID );
  151.     }
  152.     catch( OSErr err )
  153.     {
  154.         // an error occurred, or the user hit "Stop"
  155.         
  156.         if( useProgressDialog )
  157.             ForgetObject( itsPD );
  158.         
  159.         // main exception handler doesn't display alert for cancel message,
  160.         // so it is quite safe to throw it in any case
  161.         
  162.         throw err;
  163.     }
  164.     
  165.     if( useProgressDialog )
  166.         ForgetObject( itsPD );
  167. }
  168.  
  169.  
  170. /*-------------------------------***  SCAN1FOLDER  ***---------------------------------*/
  171. /*
  172. internal method that calls itself recursively to implement a scan of folders more than
  173. 1 level deep. THe recursion depth can be controlled by the searchDepth data member.
  174. ---------------------------------------------------------------------------------------*/
  175.  
  176. void    ZFolderScanner::Scan1Folder( const long dirID )
  177. {
  178.     short    index = 1;
  179.     OSErr    theErr;
  180.     
  181.     curDepth++;    // gone down a level.
  182.     
  183.     // for each item in the folder passed, either pass it to Process1File, or call
  184.     // this method again recursively.
  185.     
  186.     do
  187.     {
  188.         pb.dirInfo.ioFDirIndex = index++;    // iterate through this folder
  189.         pb.dirInfo.ioDrDirID = dirID;        // this is the folder to look in
  190.         
  191.         theErr = PBGetCatInfoSync( &pb );    
  192.         
  193.         // if the err is 'fnfErr', then this only means we tried to get more files than
  194.         // there were in the folder. Thus this is not something we should throw.
  195.         
  196.         if ((theErr != noErr) && (theErr != fnfErr))
  197.             FailOSErr( theErr );
  198.             
  199.         // what have we got there?
  200.         
  201.         if( theErr == noErr )
  202.         {
  203.             FSSpec    aSpec;
  204.             
  205.             if ((pb.hFileInfo.ioFlAttrib & ioDirMask) != 0)
  206.             {
  207.                 // another folder within this one- recurse if we have depth in hand. If
  208.                 // search depth is -1, we search as deep as necessary.
  209.                 
  210.                 if ((curDepth < searchDepth) ||
  211.                     (searchDepth == -1))
  212.                 {
  213.                     FailOSErr( FSMakeFSSpec( itsSpec.vRefNum, dirID, fName, &aSpec ));
  214.                     
  215.                     Process1Folder( aSpec );
  216.                     Scan1Folder( pb.dirInfo.ioDrDirID );
  217.                 }
  218.             }
  219.             else
  220.             {
  221.                 // a file, so call the method to handle the file. First we make a nice FSSpec
  222.                 // for the file in case we want to make an ZFile object, etc.
  223.                 
  224.                 FailOSErr( FSMakeFSSpec( itsSpec.vRefNum, dirID, fName, &aSpec ));
  225.                 Process1File( aSpec, pb.hFileInfo.ioFlFndrInfo.fdType );
  226.             }
  227.         }
  228.     }
  229.     while( theErr == noErr );
  230.     
  231.     // leaving, so decrement the level counter
  232.     
  233.     --curDepth;
  234. }
  235.  
  236. /*------------------------------***  PROCESS1FOLDER  ***-------------------------------*/
  237. /*
  238. called for every new folder before it is scanned. The default method does nothing, but
  239. you may want to override it for special uses.
  240. ---------------------------------------------------------------------------------------*/
  241.  
  242.  
  243. void    ZFolderScanner::Process1Folder( const FSSpec& aSpec )
  244. {
  245. }
  246.  
  247.  
  248. /*-------------------------------***  PROCESS1FILE  ***--------------------------------*/
  249. /*
  250. called for every file in the scanned folders. Override to do something useful with the
  251. file.
  252. ---------------------------------------------------------------------------------------*/
  253.  
  254. void    ZFolderScanner::Process1File( const FSSpec& aSpec, const OSType fType )
  255. {
  256.     // this method is called for every file found in the folder search. The default method
  257.     // doesn't do anything, but you will override this to process those files of interest.
  258.     // Note that folders are not passed to this function, only files. A common action at
  259.     // this point is to make a ZFile object with the FSSpec, then read the file, etc.
  260.     // Naturally your overridden method can really do what it likes. If you are only interested
  261.     // in particular file types, for example, you can simply ignore those that you don't want.
  262.     // if you want to use the progress dialog, you can call the inherited method to maintain
  263.     // it. By default, this passes each filename as a message, and uses the "barber-pole" style.
  264.     
  265.     if( useProgressDialog )
  266.     {
  267.         itsPD->SetMessage( aSpec.name );
  268.         FailOSErr( itsPD->InformProgress( 0 )? noErr : userCanceledErr );
  269.     }
  270. }
  271.  
  272. /*****************************************************************************/
  273. // Static methods obtained from MoreFiles- code provided by Apple DTS.
  274. /*****************************************************************************/
  275.  
  276. #pragma mark ----- File Manager Utilities -----
  277.  
  278. /*****************************************************************************/
  279.  
  280. pascal    OSErr    GetDirectoryID(short vRefNum,
  281.                                long dirID,
  282.                                StringPtr name,
  283.                                long *theDirID,
  284.                                Boolean *isDirectory)
  285. {
  286.     CInfoPBRec pb;
  287.     Str31 tempName;
  288.     OSErr error;
  289.  
  290.     /* Protection against File Sharing problem */
  291.     if ( (name == NULL) || (name[0] == 0) )
  292.     {
  293.         tempName[0] = 0;
  294.         pb.hFileInfo.ioNamePtr = tempName;
  295.         pb.hFileInfo.ioFDirIndex = -1;    /* use ioDirID */
  296.     }
  297.     else
  298.     {
  299.         pb.hFileInfo.ioNamePtr = name;
  300.         pb.hFileInfo.ioFDirIndex = 0;    /* use ioNamePtr and ioDirID */
  301.     }
  302.     pb.hFileInfo.ioVRefNum = vRefNum;
  303.     pb.hFileInfo.ioDirID = dirID;
  304.     error = PBGetCatInfoSync(&pb);
  305.     *isDirectory = (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0;
  306.     *theDirID = (*isDirectory) ? pb.dirInfo.ioDrDirID : pb.hFileInfo.ioFlParID;
  307.     return ( error );
  308. }
  309.  
  310. /*****************************************************************************/
  311.  
  312. pascal    OSErr    FSpGetDirectoryID(const FSSpec *spec,
  313.                                   long *theDirID,
  314.                                   Boolean *isDirectory)
  315. {
  316.     return ( GetDirectoryID(spec->vRefNum, spec->parID, (StringPtr)spec->name,
  317.              theDirID, isDirectory) );
  318. }
  319.  
  320.  
  321. /*****************************************************************************/
  322.  
  323. Boolean        ChooseFolder(FSSpec* folderSpec)
  324. {
  325.     Point                where = {-1, -1};
  326.     FileFilterYDUPP        ffUPP;
  327.     DlgHookYDUPP        dhUPP;
  328.     tFolderInfo            folderInfo;
  329.     OSErr                err;
  330.     short                dummyActiveList = 0;
  331.     
  332.     ffUPP = NewFileFilterYDProc((ProcPtr) GetDirFileFilter);
  333.     dhUPP = NewDlgHookYDProc((ProcPtr) GetDirDlgHook);
  334.     
  335.     folderInfo.selectHit = FALSE;
  336.     folderInfo.dirFlag = FALSE;
  337.  
  338.     CustomPutFile("\p","\p",
  339.                     &folderInfo.aReply,
  340.                     kPickFolderDialogID,
  341.                     where,
  342.                     dhUPP,
  343.                     NULL,
  344.                     &dummyActiveList,
  345.                     NULL,
  346.                     &folderInfo);
  347.                     
  348.     DisposeRoutineDescriptor(ffUPP);
  349.     DisposeRoutineDescriptor(dhUPP);
  350.     
  351.     if (folderInfo.selectHit)
  352.     {
  353.         if ( !folderInfo.aReply.sfIsFolder && !folderInfo.aReply.sfIsVolume )
  354.         {
  355.             FSSpec        tempSpec;
  356.             CInfoPBRec    infoPB;
  357.             Boolean        isDirectory;
  358.     
  359.             tempSpec = folderInfo.aReply.sfFile;
  360.             
  361.             if (tempSpec.name[0] != '\0')
  362.                 return FALSE; //err
  363.     
  364.             infoPB.dirInfo.ioNamePtr = tempSpec.name;
  365.             infoPB.dirInfo.ioVRefNum = tempSpec.vRefNum;
  366.             infoPB.dirInfo.ioDrDirID = tempSpec.parID;
  367.             infoPB.dirInfo.ioFDirIndex = -1;
  368.             
  369.             err = PBGetCatInfoSync( &infoPB );
  370.             if (err)
  371.                 return FALSE;
  372.             
  373.             tempSpec.parID = infoPB.dirInfo.ioDrParID ;
  374.             
  375.             // make sure that it's a directory
  376.             
  377.             isDirectory = (infoPB.dirInfo.ioFlAttrib & 0x10) ;
  378.             if ( !isDirectory )
  379.                 return FALSE; //err
  380.             
  381.             folderInfo.aReply.sfFile = tempSpec ;
  382.             folderInfo.aReply.sfScript = infoPB.dirInfo.ioDrFndrInfo.frScript ;
  383.             folderInfo.aReply.sfFlags = infoPB.dirInfo.ioDrUsrWds.frFlags ;
  384.             folderInfo.aReply.sfIsFolder = (tempSpec.parID == 1) ? (0x00) : (0xFF) ;
  385.             folderInfo.aReply.sfIsVolume = (tempSpec.parID == 1) ? (0xFF) : (0x00) ; ;
  386.         }
  387.  
  388.         *folderSpec = folderInfo.aReply.sfFile;
  389.  
  390.         return TRUE;
  391.     }    
  392.     return FALSE;
  393. }
  394.  
  395. /*****************************************************************************/
  396.  
  397.  
  398. static pascal Boolean    GetDirFileFilter(ParmBlkPtr pb,tFolderInfo* fInfo)
  399. {
  400.     return ((pb->fileParam.ioFlAttrib & ioDirMask) == 0);
  401. }
  402.  
  403. /*****************************************************************************/
  404.  
  405.  
  406. static pascal short        GetDirDlgHook(short item, DialogPtr theDialog, tFolderInfo* fInfo)
  407. {
  408.     short            itemType;
  409.     Handle            itemHand;
  410.     Rect            itemBox;
  411.     FSSpec            fRef;
  412.     static FSSpec    lastFile;
  413.     
  414.     if (GetWRefCon(theDialog) == (long) sfMainDialogRefCon)
  415.     {
  416.         fRef = fInfo->aReply.sfFile;
  417.         
  418.         switch (item)
  419.         {
  420.             case sfHookFirstCall:
  421.                 lastFile.vRefNum = -9999;
  422.                 break;
  423.             case sfHookLastCall:
  424.                 break;
  425.             default:
  426.                 break;
  427.             case sfHookNullEvent:
  428.                 if (! SameFile(&fRef,&lastFile))
  429.                 {
  430.                     lastFile = fRef;
  431.                     
  432.                     MakeCanonFSSpec(&fRef);    
  433.                     GetDialogItem(theDialog, kPickFolderButton, &itemType, &itemHand, &itemBox);
  434.                     SetSFButtonTitle((ControlHandle) itemHand, &fRef, &itemBox);
  435.                 }
  436.                 break;
  437.             case kPickFolderButton:
  438.                 fInfo->selectHit = TRUE;
  439.                 item = sfItemCancelButton;
  440.                 break;
  441.         }
  442.     }
  443.     return item;
  444. }
  445.  
  446. /*****************************************************************************/
  447.  
  448. static void        SetSFButtonTitle(ControlHandle theButton,FSSpec* theFile,Rect* buttonRect)
  449. {
  450.     // sets the button title to <Select ā€œ<name>ā€>, but truncated to fit the rect.
  451.     
  452.     short            width,saveSize,saveFont;
  453.     StringHandle    nStr;
  454.     Str255            bStr;
  455.     
  456.     saveSize = qd.thePort->txSize;
  457.     saveFont = qd.thePort->txFont;
  458.     
  459.     TextSize(12);
  460.     TextFont(0);
  461.     
  462.     nStr = GetString(kStdButtonTextStrID);
  463.     HLock((Handle) nStr);
  464.     CopyPString(*nStr, bStr);
  465.     HUnlock((Handle) nStr);
  466.     ReleaseResource((Handle) nStr);
  467.  
  468.     width = buttonRect->right - buttonRect->left - 32 - StringWidth(bStr);
  469.     TruncString(width, theFile->name, smTruncMiddle);
  470.     
  471.     ConcatPStrings(bStr,theFile->name);
  472.     ConcatPStrings(bStr,"\pā€");
  473.  
  474.     SetControlTitle(theButton,bStr);
  475.     ValidRect(buttonRect);
  476.     
  477.     TextFont(saveFont);
  478.     TextSize(saveSize);
  479. }
  480.  
  481. /*****************************************************************************/
  482.  
  483. static short    GetSFCurVol()
  484. {
  485.     return -(LMGetSFSaveDisk());
  486. }
  487.  
  488. /*****************************************************************************/
  489.  
  490. static long        GetSFCurDir()
  491. {
  492.     return LMGetCurDirStore();
  493. }
  494.  
  495. /*****************************************************************************/
  496.  
  497. static void        pStrInsert(StringPtr dest,StringPtr src)
  498. {
  499.     // inserts <src> at the beginning of <dest>
  500.     
  501.     BlockMoveData(dest + 1,dest + *src + 1,*dest);
  502.     BlockMoveData(src + 1,dest + 1,*src);
  503.     *dest += *src;
  504. }
  505.  
  506. /*****************************************************************************/
  507.  
  508. Boolean        GetFullPathname(FSSpec* aSpec, Str255 pathname)
  509. {
  510.     // returns the full pathname for the file spec passed. If the file does not exist, this
  511.     // returns FALSE, if it does, it returns TRUE.
  512.     
  513.     DirInfo        blk;
  514.     Str255        dirName;
  515.     OSErr        theErr;
  516.     Boolean     result = TRUE;
  517.  
  518.     pathname[0] = 0;
  519.     
  520.     blk.ioDrParID = aSpec->parID;
  521.     blk.ioNamePtr = dirName;
  522.     
  523.     do
  524.     {
  525.         blk.ioVRefNum = aSpec->vRefNum;
  526.         blk.ioFDirIndex = -1;
  527.         blk.ioDrDirID = blk.ioDrParID;
  528.         
  529.         theErr = PBGetCatInfoSync((CInfoPBPtr) &blk );
  530.         
  531.         if (theErr)
  532.         {
  533.             result = FALSE;
  534.             break;
  535.         }
  536.         
  537.         ConcatPStrings(dirName,"\p:");
  538.         pStrInsert(pathname,dirName);
  539.     }
  540.     while(blk.ioDrDirID != 2);
  541.     if (result)
  542.         ConcatPStrings(pathname,aSpec->name);
  543.     return result;
  544. }
  545.  
  546. /*****************************************************************************/
  547.  
  548.  
  549. OSErr MakeCanonFSSpec ( FSSpec *spec )
  550. {
  551.     OSErr        err ;
  552.     
  553.     err = FSMakeFSSpec( spec->vRefNum,
  554.                         spec->parID,
  555.                         spec->name,
  556.                         spec ) ;
  557.     return noErr ;
  558. }
  559.  
  560. /*****************************************************************************/
  561.  
  562. Boolean SameFile ( FSSpec *spec1, FSSpec *spec2 )
  563. {
  564.     if (spec1->vRefNum != spec2->vRefNum)
  565.         return false;
  566.     if (spec1->parID != spec2->parID)
  567.         return false;
  568.     if ( !EqualString( spec1->name, spec2->name, false, true ) )
  569.         return false;
  570.     return true;
  571. }
  572.  
  573.